package com.xiaoxin.experience.util;

import org.apache.commons.lang3.ArrayUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author xiaoxin
 */
public class XmlUtil {

    public static String xmlFormat(String originStr)
    {
        if (StringUtil.isBlank(originStr))
        {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        originStr = parseHeader(originStr,sb);
        char[] charArray = originStr.toCharArray();
        int openCount = 0;
        int closeCount = 0;
        int lastNode = -1;//0 <> 1 </>
        for (int i = 0; i < charArray.length; i++)
        {
            // < open and 换行
            if (charArray[i] == '<' && i != charArray.length - 1 && charArray[i + 1] != '/' && charArray[i + 1] != '!')
            {
                lastNode = 0;
                enterAndTabs(sb, openCount - closeCount);
                sb.append(charArray[i]);
                openCount++;
                continue;
            }

            // </ close and 换行
            if (charArray[i] == '<' && i != charArray.length - 1 && charArray[i + 1] == '/')
            {
                // 0 表示 开-闭 ,不换行
                if (lastNode != 0)
                {
                    enterAndTabs(sb, openCount - closeCount - 1);
                }
                sb.append(charArray[i]).append(charArray[++i]);
                closeCount++;
                lastNode = 1;
                continue;
            }

            // 自闭合
            if (charArray[i] == '>' && i > 0 && charArray[i - 1] == '/')
            {
                sb.append(charArray[i]);
                closeCount++;
                continue;
            }
            //反着来了
            sb.append(charArray[i]);
        }
        return sb.toString();
    }

    private static String parseHeader(String originStr, StringBuilder sb)
    {
        if (originStr.trim().startsWith("<?"))
        {
            //包含XML版本编码
            String xmlInfo = originStr.trim().substring(0, originStr.trim().indexOf("?>") + 2);
            originStr = originStr.trim().substring(originStr.trim().indexOf("?>") + 2);
            sb.insert(0,xmlInfo);
        }
        return originStr;
    }

    /**
     * 回车与制表符
     *
     * @param sb StringBuilder
     * @param tabCount 多少个制表符
     */
    private static void enterAndTabs(StringBuilder sb, int tabCount)
    {
        sb.append("\n");
        while (tabCount-- > 0)
        {
            sb.append(" ");
        }
    }

    public static Element toElement(String xmlStr)
            throws DocumentException
    {
        SAXReader reader = new SAXReader();
        reader.setEncoding("UTF-8");
        // 对海康出现的未转义字符做处理。

        String[] destStrArray = new String[]{"&lt;","&gt;","&amp;","&apos;","&quot;"};
        char despChar = '&'; // 或许可扩展兼容其他字符
        byte destBye = (byte) despChar;
        List<Byte> result = new ArrayList<>();
        byte[] rawContent = xmlStr.getBytes(StandardCharsets.UTF_8);
        for (int i = 0; i < rawContent.length; i++)
        {
            if (rawContent[i] == destBye)
            {
                boolean resul = false;
                for (String destStr : destStrArray)
                {
                    if (i + destStr.length() <= rawContent.length)
                    {
                        byte[] bytes = Arrays.copyOfRange(rawContent, i, i + destStr.length());
                        resul = resul || (Arrays.equals(bytes,destStr.getBytes()));
                    }
                }
                if (resul)
                {
                    result.add(rawContent[i]);
                }
            }
            else
            {
                result.add(rawContent[i]);
            }
        }
        Byte[] bytes = new Byte[0];
        byte[] bytesResult = ArrayUtils.toPrimitive(result.toArray(bytes));

        Document xml = reader.read(new ByteArrayInputStream(bytesResult));
        return xml.getRootElement();
    }
}
